/**
 * Copyright (c) 2016-2020 https://github.com/zhaohuatai
 *
 * contact z_huatai@qq.com
 *  
 */
package org.zfes.snowy.core.util;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

import org.zfes.snowy.core.util.datex.ZDateStyle;

public class ZObjectUtil extends org.springframework.util.ObjectUtils {
    /**
     * <p>Returns a default value if the object passed is {@code null}.</p>
     *
     * <pre>
     * ObjectUtils.defaultIfNull(null, null)      = null
     * ObjectUtils.defaultIfNull(null, "")        = ""
     * ObjectUtils.defaultIfNull(null, "zz")      = "zz"
     * ObjectUtils.defaultIfNull("abc", *)        = "abc"
     * ObjectUtils.defaultIfNull(Boolean.TRUE, *) = Boolean.TRUE
     * </pre>
     *
     * @param <T> the type of the object
     * @param object  the {@code Object} to test, may be {@code null}
     * @param defaultValue  the default value to return, may be {@code null}
     * @return {@code object} if it is not {@code null}, defaultValue otherwise
     */
    public static <T> T defaultIfNull(final T object, final T defaultValue) {
        return org.apache.commons.lang3.ObjectUtils.defaultIfNull(object, defaultValue);
    }
    
    
    
	public static <T> List<T>  emptyListIfNull(final List<T> object){
        return org.apache.commons.lang3.ObjectUtils.defaultIfNull(object, Collections.emptyList());
    }
    
    /**
     * <p>Returns the first value in the array which is not {@code null}.
     * If all the values are {@code null} or the array is {@code null}
     * or empty then {@code null} is returned.</p>
     *
     * <pre>
     * ObjectUtils.firstNonNull(null, null)      = null
     * ObjectUtils.firstNonNull(null, "")        = ""
     * ObjectUtils.firstNonNull(null, null, "")  = ""
     * ObjectUtils.firstNonNull(null, "zz")      = "zz"
     * ObjectUtils.firstNonNull("abc", *)        = "abc"
     * ObjectUtils.firstNonNull(null, "xyz", *)  = "xyz"
     * ObjectUtils.firstNonNull(Boolean.TRUE, *) = Boolean.TRUE
     * ObjectUtils.firstNonNull()                = null
     * </pre>
     *
     * @param <T> the component type of the array
     * @param values  the values to test, may be {@code null} or empty
     * @return the first value from {@code values} which is not {@code null},
     *  or {@code null} if there are no non-null values
     * 
     */
    @SuppressWarnings("unchecked")
	public static <T> T firstNonNull(final T... values) {
    	return org.apache.commons.lang3.ObjectUtils.firstNonNull(values);
    }
    public static boolean equal( Object a,  Object b) {
    	return Objects.equals(a, b);
    }
    public static boolean equalDeep( Object a,  Object b) {
    	return Objects.deepEquals(a, b);
    }
    
    public static boolean notEqual(final Object object1, final Object object2) {
        return equal(object1, object2) == false;
    }
    
    public static boolean isNull( Object object1) {
        return Objects.isNull(object1);
    }
    public static boolean isNotNull(Object obj) {
        return Objects.nonNull(obj);
    }
    
    public static boolean isEmpty(Optional<?> op) {
        return op == null||!op.isPresent();
    }
    public static boolean isNotEmpty(Optional<?> op) {
        return op != null&&op.isPresent();
    }
    public static boolean isNotEmpty(Object obj) {
    	return  !isEmpty(obj);
    }
    //---------------------------------------------------------------------
    
    public static <T> Optional<T> castObj(Class<T> clazz, Object object) {
    	if(Objects.nonNull(object)){
    		return Optional.of(object).filter(clazz::isInstance).map(clazz::cast);
    		//return Optional.of(object).map(clazz::cast);
    	}
    	return  Optional.empty();
    }
    public static <T> Optional<T> castObj(Class<T> clazz, Optional<Object> object) {
    	if(Objects.nonNull(object)){
    		return object.filter(clazz::isInstance).map(clazz::cast);
    	}
    	return  Optional.empty();
    }
    public static boolean canCast(Class<?> clazz, Optional<Object> object) {
    	if(Objects.nonNull(object)){
    		return object.filter(clazz::isInstance).isPresent();
    	}
    	return  false;
    }
    public static boolean canCast(Class<?> clazz,Object object) {
    	if(Objects.nonNull(object)){
    		return Optional.of(object).filter(clazz::isInstance).isPresent();
    	}
    	return  false;
    }

    
    public static Optional<String> castStr( Object object) {
    	if(Objects.nonNull(object)){
    		return castObj(String.class,  object);
    	}
    	return Optional.empty();
    }
    
    public static Optional<String> castStr(Optional<Object> object) {
    	if(object.isPresent()){
    		return castObj(String.class,  object);
    	}
    	return Optional.empty();
    }
 //--------------------------------------------------------------------------   
    public static Optional<Integer> castInteger( Object object) {
    	if(Objects.nonNull(object)){
    		return castObj(Integer.class,  object);
    	}
    	return Optional.empty();
    }
    
    public static Optional<Integer> castInteger(Optional<Object> object) {
    	if(object.isPresent()){
    		return castObj(Integer.class,  object);
    	}
    	return Optional.empty();
    }
    public static Optional<Integer> toInteger( Object object) {
    	if(Objects.nonNull(object)){
    		try{
    			return Optional.of(Integer.valueOf(ZStrUtil.trim(object.toString())));
    		}catch (Exception e) {
    			return Optional.empty();
			}
    	}
    	return Optional.empty();
    }
    public static Optional<Integer> toInteger(Optional<Object> object) {
    	if(object.isPresent()){
    		return toInteger(object.get());
    	}
    	return Optional.empty();
    }
//--------------------------------------------------------------------------     
    
    public static Optional<Long> castLong( Object object) {
    	if(Objects.nonNull(object)){
    		return castObj(Long.class,  object);
    	}
    	return Optional.empty();
    }

    public static Optional<Long> castLong(Optional<Object> object) {
    	if(object.isPresent()){
    		return castObj(Long.class,  object);
    	}
    	return Optional.empty();
    }
    
    public static Optional<Long> toLong( Object object) {
    	if(Objects.nonNull(object)){
    		try{
    			return Optional.of(Long.valueOf(ZStrUtil.trim(object.toString())));
    		}catch (Exception e) {
    			return Optional.empty();
			}
    	}
    	return Optional.empty();
    }
    public static Optional<Long> toLong(Optional<Object> object) {
    	if(object.isPresent()){
    		return toLong(object.get());
    	}
    	return Optional.empty();
    }
    
//--------------------------------------------------------------------------     
    public static Optional<Double> castDouble( Object object) {
    	if(Objects.nonNull(object)){
    		return castObj(Double.class,  object);
    	}
    	return Optional.empty();
    }
 
    public static Optional<Double> castDouble(Optional<Object> object) {
    	if(object.isPresent()){
    		return castObj(Double.class,  object);
    	}
    	return Optional.empty();
    }
    
    public static Optional<Double> toDouble( Object object) {
    	if(Objects.nonNull(object)){
    		try{
    			return Optional.of(Double.valueOf(ZStrUtil.trim(object.toString())));
    		}catch (Exception e) {
    			return Optional.empty();
			}
    	}
    	return Optional.empty();
    }
    public static Optional<Double> toDouble(Optional<Object> object) {
    	if(object.isPresent()){
    		return toDouble(object.get());
    	}
    	return Optional.empty();
    } 
//--------------------------------------------------------------------------     
    public static Optional<Float> castFloat( Object object) {
    	if(Objects.nonNull(object)){
    		return castObj(Float.class,  object);
    	}
    	return Optional.empty();
    }

    public static Optional<Float> castFloat(Optional<Object> object) {
    	if(object.isPresent()){
    		return castObj(Float.class,  object);
    	}
    	return Optional.empty();
    }
    public static Optional<Float> toFloat( Object object) {
    	if(Objects.nonNull(object)){
    		try{
    			return Optional.of(Float.valueOf(ZStrUtil.trim(object.toString())));
    		}catch (Exception e) {
    			return Optional.empty();
			}
    	}
    	return Optional.empty();
    }
    public static Optional<Float> toFloat(Optional<Object> object) {
    	if(object.isPresent()){
    		return toFloat(object.get());
    	}
    	return Optional.empty();
    }
//--------------------------------------------------------------------------     
    public static Optional<Short> castShort( Object object) {
    	if(Objects.nonNull(object)){
    		return castObj(Short.class,  object);
    	}
    	return Optional.empty();
    }

    public static Optional<Short> castShort(Optional<Object> object) {
    	if(object.isPresent()){
    		return castObj(Short.class,  object);
    	}
    	return Optional.empty();
    }
    public static Optional<Short> toShort( Object object) {
    	if(Objects.nonNull(object)){
    		try{
    			return Optional.of(Short.valueOf(ZStrUtil.trim(object.toString())));
    		}catch (Exception e) {
    			return Optional.empty();
			}
    	}
    	return Optional.empty();
    }
    public static Optional<Short> toShort(Optional<Object> object) {
    	if(object.isPresent()){
    		return toShort(object.get());
    	}
    	return Optional.empty();
    }
//--------------------------------------------------------------------------      
    
    public static Optional<Byte> castByte( Object object) {
    	if(Objects.nonNull(object)){
    		return castObj(Byte.class,  object);
    	}
    	return Optional.empty();
    }

    public static Optional<Byte> castByte(Optional<Object> object) {
    	if(object.isPresent()){
    		return castObj(Byte.class,  object);
    	}
    	return Optional.empty();
    }
    public static Optional<Byte> toByte( Object object) {
    	if(Objects.nonNull(object)){
    		try{
    			return Optional.of(Byte.valueOf(ZStrUtil.trim(object.toString())));
    		}catch (Exception e) {
    			return Optional.empty();
			}
    	}
    	return Optional.empty();
    }
    public static Optional<Byte> toByte(Optional<Object> object) {
    	if(object.isPresent()){
    		return toByte(object.get());
    	}
    	return Optional.empty();
    } 
//-------------------------------------------------------------------------- 
    public static Optional<Boolean> castBoolean( Object object) {
    	if(Objects.nonNull(object)){
    		return castObj(Boolean.class,  object);
    	}
    	return Optional.empty();
    }

    public static Optional<Boolean> castBoolean(Optional<Object> object) {
    	if(object.isPresent()){
    		return castObj(Boolean.class,  object);
    	}
    	return Optional.empty();
    }
    public static Optional<Boolean> toBoolean( Object object) {
    	if(Objects.nonNull(object)){
    		if(TRUE_VALUES.contains(ZStrUtil.trim(object.toString()))){
				return Optional.of(true);
			}
			if(FALSE_VALUES.contains(ZStrUtil.trim(object.toString()))){
				return Optional.of(false);
			}
			return Optional.empty();
    	}
    	return Optional.empty();
    }
    public static Optional<Boolean> toBoolean(Optional<Object> object) {
    	if(object.isPresent()){
    		return toBoolean(object.get());
    	}
    	return Optional.empty();
    } 
//--------------------------------------------------------------------------     
    public static Optional<Date> castDate( Object object) {
    	if(Objects.nonNull(object)){
    		return castObj(Date.class,  object);
    	}
    	return Optional.empty();
    }

    public static Optional<Date> castDate(Optional<Object> object) {
    	if(object.isPresent()){
    		return castObj(Date.class,  object);
    	}
    	return Optional.empty();
    }
    public static Optional<Date> toDate( Object object) {
    	if(Objects.nonNull(object)){
    		try{
    			return Optional.of(ZDateUtil.parseDate(ZStrUtil.trim(object.toString()), ZDateStyle.YYYY_MM_DD_HH_MM_SS));
    		}catch (Exception e) {
    			return Optional.empty();
			}
    	}
    	return Optional.empty();
    }
    public static Optional<Date> toDate(Optional<Object> object) {
    	if(object.isPresent()){
    		return toDate(object.get());
    	}
    	return Optional.empty();
    } 
//--------------------------------------------------------------------------     
    
    public static Optional<BigDecimal> castBigDecimal( Object object) {
    	if(Objects.nonNull(object)){
    		return castObj(BigDecimal.class,  object);
    	}
    
    	return Optional.empty();
    }

    public static Optional<BigDecimal> castBigDecimal(Optional<Object> object) {
    	if(object.isPresent()){
    		return castObj(BigDecimal.class,  object);
    	}
    	return Optional.empty();
    }
    public static Optional<BigDecimal> toBigDecimal( Object object) {
    	if(Objects.nonNull(object)){
    		String str=ZStrUtil.trim(object.toString());
    		try{
    			if(str.contains(".")){
    				Optional<Double> res=toDouble(str);
    				if(!res.isPresent()){
    					return Optional.empty();
    				}
    				return Optional.of(BigDecimal.valueOf(res.get()));
    			}else{
    				Optional<Long> res=toLong(str);
    				if(!res.isPresent()){
    					return Optional.empty();
    				}
    				return Optional.of(BigDecimal.valueOf(res.get()));
    			}
    		}catch (Exception e) {
    			return Optional.empty();
			}
    	}
    	return Optional.empty();
    }
    public static Optional<BigDecimal> toBigDecimal(Optional<Object> object) {
    	if(object.isPresent()){
    		return toBigDecimal(object.get());
    	}
    	return Optional.empty();
    } 
//--------------------------------------------------------------------------     
    public static Optional<byte[]> castByteArray( Object object) {
    	if(Objects.nonNull(object)){
    		return castObj(byte[].class,  object);
    	}
    	return Optional.empty();
    }

    public static Optional<byte[]> castByteArray(Optional<Object> object) {
    	if(object.isPresent()){
    		return castObj(byte[].class,  object);
    	}
    	return Optional.empty();
    }
     
	public static final Set<String> TRUE_VALUES = new HashSet<String>(4);

	public static final Set<String> FALSE_VALUES = new HashSet<String>(4);

	static {
		TRUE_VALUES.add("true");
		TRUE_VALUES.add("on");
		TRUE_VALUES.add("yes");
		TRUE_VALUES.add("1");
		
		TRUE_VALUES.add("TRUE");
		TRUE_VALUES.add("ON");
		TRUE_VALUES.add("YES");
		

		FALSE_VALUES.add("false");
		FALSE_VALUES.add("off");
		FALSE_VALUES.add("no");
		FALSE_VALUES.add("0");
		
		FALSE_VALUES.add("FALSE");
		FALSE_VALUES.add("OFF");
		FALSE_VALUES.add("NO");
	}
}
